﻿using AutoTask.Collect;
using AutoTask.Model;
using RpcHelper;
using AutoTask.Service.Helper;
using RpcClient;
using AutoTask.Collect.Model;

namespace AutoTask.Service.AutoTask
{
    /// <summary>
    /// 任务控制器
    /// </summary>
    internal class SingleTask
    {
        private int _Status = 0;
        public SingleTask(Guid id, int verNum)
        {
            Id = id;
            VerNum = verNum;
        }
        private IAutoTaskCollect _TaskCollect
        {
            get => RpcClient.RpcClient.Unity.Resolve<IAutoTaskCollect>();
        }

        private ITaskItemCollect _TaskItemCollect
        {
            get => RpcClient.RpcClient.Unity.Resolve<ITaskItemCollect>();
        }
        private ITaskPlanCollect _TaskPlanCollect
        {
            get => RpcClient.RpcClient.Unity.Resolve<ITaskPlanCollect>();
        }
        private IAutoTaskLogCollect _TaskLogCollect
        {
            get => RpcClient.RpcClient.Unity.Resolve<IAutoTaskLogCollect>();
        }
        public Guid Id { get; }

        private RemoteTask _Task;

        private AutoTaskPlan[] _Plans;

        private AutoTaskItem[] _Items;

        private volatile int _ExecTime = 0;

        private DateTime _NextExecTime;

        private volatile int _ExecVerNum = 0;

        private int _TaskNum = 0;

        public int VerNum
        {
            get;
            private set;
        }

        public int TaskPriority
        {
            get;
            private set;
        }
        public bool Init()
        {
            if (_InitTask())
            {
                Interlocked.Exchange(ref _Status, 1);//启用
                return true;
            }
            return false;
        }
        private void _InitState()
        {
            TaskState state = this._TaskCollect.GetTaskState(Id);
            if (state.NextExecTime.HasValue)
            {
                _NextExecTime = _Plans.GetExecTime(state.NextExecTime.Value);
                _ExecTime = HeartbeatTimeHelper.GetTime(_NextExecTime);
            }
            _ExecVerNum = state.ExecVerNum;
        }
        private bool _InitTask()
        {
            if (!_Init(out string error))
            {
                this._TaskCollect.StopTask(Id, error);
                return false;
            }
            _InitState();
            if (_ExecTime == 0)
            {
                DateTime time = _Plans.GetExecTime(_NextExecTime);
                this._TaskCollect.SetTaskTime(Id, time);
                _NextExecTime = time;
                _ExecTime = HeartbeatTimeHelper.GetTime(time);
            }
            return true;
        }
        private bool _Init(out string error)
        {
            this._Task = this._TaskCollect.GetTask(Id, VerNum);
            this._Plans = this._TaskPlanCollect.Gets(Id, VerNum);
            if (this._Plans.Length == 0)
            {
                error = "task.plan.null";
                return false;
            }
            AutoTaskItem[] items = this._TaskItemCollect.Gets(Id, VerNum);
            if (items.Length == 0)
            {
                error = "task.item.null";
                return false;
            }
            _Items = items.OrderBy(a => a.ItemSort).ToArray();
            if (!this._Plans.CheckPlan())
            {
                int len= this._TaskPlanCollect.StopTaskPlan(this._Plans.ConvertMap<AutoTaskPlan,TaskPlanResult>(c=>c.IsEnable==false));
                if (len== this._Plans.Length)
                {
                    error = "task.plan.null";
                    return false;
                }
                this._Plans = this._Plans.Remove(c => c.IsEnable == false);
            }
            this.TaskPriority = this._Task.TaskPriority;
            error = null;
            return true;
        }

        public int ExecTask(int time)
        {
            if (this._ExecTime == 0 || this._ExecTime > time)
            {
                return int.MaxValue;
            }
            else if (Interlocked.CompareExchange(ref this._Status, 4, 1) == 1)
            {
                DateTime next = this._Plans.GetNextTime(DateTime.Now);
                this._ExecTime = HeartbeatTimeHelper.GetTime(next);
                if (this._TaskCollect.BeginExec(this.Id, next, this._ExecVerNum, out int ver))
                {
                    this._ExecVerNum = ver;
                    this._BeginExec(_Task.BeginItem);
                }
                else
                {
                    this._InitState();
                }
            }
            return this._ExecTime;
        }
        private void _EndTask()
        {
            if (Interlocked.CompareExchange(ref this._Status, 1, 4) == 4)
            {
                this._TaskCollect.ExecEnd(this.Id);
            }
        }
        private void _BeginExec(int index)
        {
            AutoTaskItem[] items = _Items.FindAll(c => c.ItemSort == index);
            if (items.Length > 0)
            {
                Interlocked.Exchange(ref this._TaskNum, items.Length);
                items.ForEach(a => this._ExecItem(a));
            }
            else
            {
                this._EndTask();
            }
        }
        private void _RootEnd()
        {
            int num = Interlocked.Add(ref _TaskNum, -1);
            if (num == 0)
            {
                this._EndTask();
            }
        }
        private void _Exec(int index)
        {
            AutoTaskItem[] items = this._Items.FindAll(c => c.ItemSort == index);
            if (items.Length == 1)
            {
                this._ExecItem(items[0]);
            }
            else if (items.Length > 0)
            {
                Interlocked.Add(ref this._TaskNum, items.Length - 1);
                items.ForEach(a => this._ExecItem(a));
            }
            else
            {
                this._RootEnd();
            }
        }
        private void _TaskFail(AutoTaskItem item)
        {
            if (item.FailSetup == TaskSetup.停止执行)
            {
                this._RootEnd();
                return;
            }
            else if (item.FailSetup == TaskSetup.继续下一步)
            {
                this._Exec(item.ItemSort + 1);
            }
            else
            {
                this._Exec(item.FailNextSetup.Value);
            }
        }
        private void _TaskSuccess(AutoTaskItem item)
        {
            if (item.SuccessSetup == TaskSetup.停止执行)
            {
                this._RootEnd();
                return;
            }
            else if (item.SuccessSetup == TaskSetup.继续下一步)
            {
                this._Exec(item.ItemSort + 1);
            }
            else
            {
                this._Exec(item.NextSetup.Value);
            }
        }
        private async void _ExecItem(AutoTaskItem item)
        {
            await Task.Run(() =>
            {
                TaskExecLog[] logs = item.ExecTask(this._Task.RegionId);
                this._TaskLogCollect.Add(logs, item, this.Id);
                TaskExecLog[] fail = logs.FindAll(c => c.isFail);
                bool isSuccess = true;
                long? error = null;
                if (fail.Length != 0)
                {
                    isSuccess = false;
                    error = fail[fail.Length - 1].errorId;
                }
                this._TaskItemCollect.ExecComplate(new SyncItemResult
                {
                    ItemId = item.Id,
                    IsSuccess = isSuccess,
                    ErrorCode = error
                });
                if (isSuccess)
                {
                    this._TaskSuccess(item);
                }
                else
                {
                    this._TaskFail(item);
                }

            });
        }
        public void StopTask()
        {
            Interlocked.Exchange(ref this._Status, 2);
        }
        public void Reset(int verNum)
        {
            this.VerNum = verNum;
            int status = Interlocked.CompareExchange(ref this._Status, 0, 1);
            if (status == 1)
            {
                if (!this._InitTask())
                {
                    this.StopTask();
                    return;
                }
                Interlocked.Exchange(ref this._Status, 1);//启用
            }
        }
    }
}
